home *** CD-ROM | disk | FTP | other *** search
- /*
- * B G _ G R A F 2 . C
- * A graphics module for the backgammon playing program BG.C.
- * This version 14th January 1993
- *
-
- The computer idea of the graphics of the board is based on BLACK where
- the two runners of black are at the top left, i.e the initial position is:
- 0 1 2 3 4 5 6 7 8 9 10 11 12 13 <-- grid col
- 25 1 2 3 4 5 6 0 7 8 9 10 11 12 <-- index in layout
- H B W | W B 0
- B whites W | W B 1
- O inner W | W B 2
- table W B B 3
- M W | B 4
- -------------------A------------------- 5,6
- E B | W 7
- blacks B R W 8
- inner B | B W 9
- W table B | B W 10
- W B | B W 11
- 25 24 23 22 21 20 19 0 18 17 16 15 14 13 <-- index in layout
-
- The pieces are on a grid of GRID_ROWS by GRID_COLS. See also comments
- in BG_GRAF1.C. We use the above numbering system, BLACK going home means
- increasing indices.
- *************************************************************************/
-
- #include "comp.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <bios.h>
- #include <conio.h>
- #include "mousy.h"
- #include "bg.h"
-
- /*************************************************************************/
-
- extern Screen_Const_t Grafs ;
- extern Disp_Cfg_t Disp_Cfg ;
-
- /* Private function prototypes */
-
- static void Get_Grid_Point_Top (short* Grid_Col, short* Grid_Row,
- Player_t Player, short Point_Num) ;
- static void Get_Grid_Point (short* Grid_Col, short* Grid_Row,
- Player_t Player, short Point_Num,
- short Piece_Row) ;
- static void Draw_Point_Triangle (Player_t Player, short Point_Num) ;
- static void Draw_Point_And_Pieces (Player_t Plyr, short Num, char n) ;
- static void Draw_Pieces (Player_t Player, short Num, char n) ;
- static void Draw_Piece (short x, short y, Player_t Player) ;
-
- /*************************************************************************/
-
- void Get_Pbox_Coords (short Box_Coords[2][2],
- Player_t Player, short Point_Num)
- /*
- PURPOSE: To return the opposite corners of the box which is at Point_Num.
- NOTES : 1) The returned coords include pixels you can draw on top of.
- 2) A very important idea is represented by this function, i.e.
- that of an area within which you can draw. If you go out of
- this area you may destroy parts of dividing lines or text areas.
- */
- {
- short Row0,Col0,Row1,Col1 ;
-
- Get_Grid_Point_Top (&Col0,&Row0,Player,Point_Num) ;
- /* Get the grid square at opposite corner (bottom right) */
- Col1 = Col0 + 1 ;
- Row1 = Row0 + (GRID_ROWS/2) ;
-
- Get_Grid_Corner (&Box_Coords[0][XI],&Box_Coords[0][YI],Col0,Row0) ;
- Get_Grid_Corner (&Box_Coords[1][XI],&Box_Coords[1][YI],Col1,Row1) ;
- Box_Coords [0][XI]++ ;
- Box_Coords [0][YI]++ ;
- Box_Coords [1][XI]-- ;
- Box_Coords [1][YI]-- ;
- }
-
- /***************************************************************************/
-
- #define ODD(a) (((a/2)*2) != a)
-
- static void Draw_Point_Triangle (Player_t Player, short Point_Num)
- /*
- PURPOSE: To draw the triangle of the players point, erasing what was
- there before.
- NOTES: The triangle will be either of the following:
- 0 1 2
- 1 2 0
- upwards downwards
- */
- {
- short A_Box [2][2] ; /* Defines box of the point */
- short Mid_X ; /* X coord of the tip of the triangle */
- short Trig[6] ; /* The triangle we create then draw */
- short Point_Colour ;
-
- Get_Pbox_Coords (A_Box,Player,Point_Num) ;
-
- Clear_Point (Player,Point_Num) ;
- Mid_X = (A_Box[0][XI] + A_Box[1][XI]) / 2 ;
- if (!Disp_Cfg.Colour) {
- Point_Colour = WHITE ;
- } else if ((EVEN(Point_Num) && (Player == BLACK_PLAYER)) ||
- (ODD(Point_Num) && (Player == WHITE_PLAYER))) {
- Point_Colour = RED ;
- } else {
- Point_Colour = GREEN ;
- }
- if (((Player == BLACK_PLAYER) && (Point_Num >= 13)) ||
- ((Player == WHITE_PLAYER) && (Point_Num <= 12))) {
- /* An upward pointing triangle */
- Trig [0] = Mid_X ;
- Trig [1] = A_Box[0][YI] ;
- Trig [2] = A_Box[0][XI] ;
- Trig [3] = A_Box[1][YI] ;
- Trig [4] = A_Box[1][XI] ;
- Trig [5] = A_Box[1][YI] ;
- } else {
- /* A downwards pointing triangle */
- Trig [0] = Mid_X ;
- Trig [1] = A_Box[1][YI] ;
- Trig [2] = A_Box[0][XI] ;
- Trig [3] = A_Box[0][YI] ;
- Trig [4] = A_Box[1][XI] ;
- Trig [5] = A_Box[0][YI] ;
- }
-
- if (Disp_Cfg.Colour) {
- Fill_Poly (3,Trig,Point_Colour) ;
- } else {
- short i,c1,c2 ;
- for (i = 0 ; i < 3 ; i++) {
- c1 = i * 2 ;
- c2 = ((i + 1) % 3) * 2 ;
- Draw_Line (Trig[c1],Trig[c1+1],
- Trig[c2],Trig[c2+1],WHITE) ;
- }
- }
- }
-
- /***************************************************************************/
-
- static void Draw_Point_And_Pieces (Player_t Player, short Point_Num, char N_Pieces)
- /*
- PURPOSE: To draw the point and the pieces of the player on the point.
- */
- {
- if ((Point_Num == HOME_I) || (Point_Num == BAR_I)) {
- Clear_Point (Player,Point_Num) ;
- } else {
- Draw_Point_Triangle (Player,Point_Num) ;
- }
- Draw_Pieces (Player,Point_Num,N_Pieces) ;
- }
-
- /***************************************************************************/
-
- static void Draw_Pieces (Player_t Player, short Point_Num, char N_Pieces)
- /*
- PURPOSE: To draw the pieces of the player on the point.
- NOTES : 1) If there are Rows_Available pieces or less we are
- ok and all pieces will fit on the triangle, otherwise
- we have to do some fancy footwork, overlapping them.
- 2) Here we should reverse the doodahs when doing the bar.
- */
- {
- short X_Center,Y_Center,Nth_Row,Rows_Available ;
-
- Nth_Row = 0 ;
- if (Point_Num == BAR_I) {
- /* Leave space for T and D cells in center of bar, plus
- doubling cube at bottom or top */
- Rows_Available = (GRID_ROWS/2) - 2 ;
- } else {
- Rows_Available = GRID_ROWS/2 ;
- }
- while (Nth_Row < N_Pieces) {
- Get_Piece_Center (&X_Center,&Y_Center,Player,Point_Num,Nth_Row,
- N_Pieces,Rows_Available) ;
- Draw_Piece (X_Center,Y_Center,Player) ;
- Nth_Row ++ ;
- }
- }
-
- /***************************************************************************/
-
- static void Draw_Piece (short x, short y, Player_t Player)
- /*
- PURPOSE: To draw a piece centred on x,y.
- */
- {
- if (Disp_Cfg.Colour) {
- if (Player == BLACK_PLAYER) {
- Fill_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,LIGHTRED) ;
- Draw_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,WHITE) ;
- } else {
- Fill_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,WHITE) ;
- Draw_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,BLACK) ;
- }
- } else {
- if (Player == BLACK_PLAYER) {
- Draw_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,WHITE) ;
- } else {
- Fill_Ellipse (x,y,Grafs.Unit_Wide/2,Grafs.Unit_High/2,WHITE) ;
- }
- }
- }
-
- /***************************************************************************/
-
- static void Get_Grid_Point_Top (short* Grid_Col, short* Grid_Row,
- Player_t Player, short Point_Num)
- /*
- PURPOSE: Given the point number for the player return the grid row and
- column of the grid box closest to the top of the board, see
- diagram at the top of this file.
- NOTES: 1) The home and bar 'points' (areas) are also valid
- */
- {
- short Row,Col ;
- boolean Col_Done ;
-
- if (Point_Num == BAR_I) {
- Col = BAR_COL ;
- Col_Done = TRUE ;
- } else if (Point_Num == HOME_I) {
- Col = HOME_COL ;
- Col_Done = TRUE ;
- } else {
- Col_Done = FALSE ;
- }
-
- if (Player == WHITE_PLAYER) {
- /* We work always with black */
- Point_Num = Reverse_Index (Point_Num) ;
- }
- if (Point_Num >= 13) {
- /* We are in the lower half of the board */
- if (!Col_Done) {
- Col = 25 - Point_Num ;
- }
- Row = GRID_ROWS/2 ;
- } else {
- /* We are in the upper half */
- if (!Col_Done) {
- Col = Point_Num ;
- }
- Row = 0 ;
- }
- if ((Col > 6) && (!Col_Done)) {
- Col++ ; /* Skip the area of the bar */
- }
- *Grid_Col = Col ;
- *Grid_Row = Row ;
- }
-
- /***************************************************************************/
-
- static void Get_Grid_Point (short* Grid_Col, short* Grid_Row,
- Player_t Player, short Point_Num,
- short Piece_Row)
- /*
- PURPOSE: Given the Player, the Point_Num, and the Piece_Row (how far
- the piece is from the edge of the board) we return the Grid
- row and column. This means of course that as Piece_Row gets
- higher we get closer to the vertical center of the board.
- NOTES: 1) This function does NOT 'squash' the pieces if there are
- too many of them in a column.
- */
- {
- short Row,Col ;
-
- if (Point_Num != BAR_I) {
- boolean Col_Done ;
-
- if (Point_Num == HOME_I) {
- Col = HOME_COL ;
- Col_Done = TRUE ;
- } else {
- Col_Done = FALSE ;
- }
- if (Player == WHITE_PLAYER) {
- /* We work always with black */
- Point_Num = Reverse_Index (Point_Num) ;
- }
- if (Point_Num >= 13) {
- /* We are in the lower half of the board */
- if (!Col_Done) {
- Col = 25 - Point_Num ;
- }
- Row = (GRID_ROWS-1) - Piece_Row ;
- } else {
- /* We are in the upper half */
- if (!Col_Done) {
- Col = Point_Num ;
- }
- Row = Piece_Row ;
- }
- if ((Col > 6) && (!Col_Done)) {
- Col++ ; /* Skip the area of the bar */
- }
-
- } else {
- /* On the BAR, we need to work from center outwards, leaving
- two cells in the center for the doubling cube button */
- Col = BAR_COL ;
- Piece_Row ++ ; /* Leave a bit of space in center */
- if (Player == WHITE_PLAYER) {
- Row = (GRID_ROWS/2) + Piece_Row ;
- } else {
- Row = ((GRID_ROWS/2)-1) - Piece_Row ;
- }
- }
- *Grid_Col = Col ;
- *Grid_Row = Row ;
- }
-
- /***************************************************************************/
-
- static Layout_t Start_Graf_Layout = {20,
- 20,20,20,20,20,20,
- 20,20,20,20,20,20,
- 20,20,20,20,20,20,
- 20,20,20,20,20,20,
- 20} ;
-
- /* 'Old' layouts initialised to this pattern when you want */
- /* to force a full redraw, see below */
-
- /****************************************************************************/
-
- Layout_t Old_Black, Old_White ; /* What we drew before */
-
- void Draw_Board_Full (Layout_t Lays[N_PLAYERS])
- /*
- PURPOSE: To update the board fully, drawing all points.
- */
- {
- extern boolean No_Cube ;
- short p,x,y ;
- short A_Box [2][2] ; /* Defines box of the point */
-
- /* Erase the previous board */
- Fill_Rect (0,0,Grafs.Board_Wide,Grafs.Board_High,BLACK) ;
- /* Draw edge of the board */
- Draw_Rect (0,0,Grafs.Board_Wide+2,Grafs.Board_High+2,WHITE) ;
- /* Since the board has Board_Wide pixels the frame must be 2 pixels
- bigger */
-
- /* Draw the home line */
- Get_Grid_Corner (&x,&y,HOME_COL+1,0) ;
- Draw_Line (x,y,x,y+Grafs.Board_High-1,WHITE) ;
-
- /* Draw the bar lines
- Get_Grid_Corner (&x,&y,BAR_COL,0) ;
- Draw_Line (x,y,x,y+Grafs.Board_High-1,WHITE) ;
- Get_Grid_Corner (&x,&y,BAR_COL+1,0) ;
- Draw_Line (x,y,x,y+Grafs.Board_High-1,WHITE) ; */
-
- Get_Pbox_Coords (A_Box,BLACK_PLAYER,BAR_I) ;
- Draw_Line (A_Box[0][XI]-1,A_Box[0][YI]-1,
- A_Box[0][XI]-1,A_Box[0][YI]-1+Grafs.Board_High-1,
- WHITE) ;
- Draw_Line (A_Box[1][XI]+1,A_Box[0][YI]-1,
- A_Box[1][XI]+1,A_Box[0][YI]-1+Grafs.Board_High-1,
- WHITE) ;
-
- for (p=0 ; p < N_PLACES ; p++) {
- Old_White[p] = Start_Graf_Layout[p] ;
- Old_Black[p] = Start_Graf_Layout[p] ;
- }
-
- Draw_Board_Quick (Lays[BLACK_PLAYER],Lays[WHITE_PLAYER]) ;
-
- if (!No_Cube) {
- Draw_Double_Button () ;
- }
- }
-
- /***************************************************************************/
-
- void Draw_Board_Quick (Layout_t Black, Layout_t White)
- /*
- PURPOSE: To update the board quickly, drawing only the points
- which have changed.
- NOTES : The loop with pb and pw: Remember that they are both two
- views of the same thing.
- */
- {
- short pb,pw ;
- boolean Bar_Change ;
-
- Clear_Help_Line () ;
-
- /* Draw the pieces which are on points */
- for (pw = 1 ; pw <= POINT24_I ; pw++) {
- pb = Reverse_Index (pw) ;
- if ((White[pw] != Old_White[pw]) ||
- (Black[pb] != Old_Black[pb])) {
- if (Black[pb] > 0) {
- Draw_Point_And_Pieces (BLACK_PLAYER,pb,Black[pb]) ;
- } else if (White[pw] > 0) {
- Draw_Point_And_Pieces (WHITE_PLAYER,pw,White[pw]) ;
- } else {
- Draw_Point_Triangle (WHITE_PLAYER,pw) ;
- }
- }
- }
-
- /* Draw any changes on the bar */
- Bar_Change = FALSE ;
- if (White[BAR_I] != Old_White[BAR_I]) {
- Draw_Point_And_Pieces (WHITE_PLAYER,BAR_I,White[BAR_I]) ;
- Bar_Change = TRUE ;
- }
- if (Black[BAR_I] != Old_Black[BAR_I]) {
- Draw_Point_And_Pieces (BLACK_PLAYER,BAR_I,Black[BAR_I]) ;
- Bar_Change = TRUE ;
- }
- if (Bar_Change) {
- extern int Double_Value ;
- extern Player_t Double_Possessor ;
- extern boolean No_Cube ;
- if (!No_Cube) {
- Draw_Double_Button () ;
- Draw_Double_Cube (Double_Possessor,Double_Value) ;
- }
- }
-
- /* Draw any changes at home */
- if (White[HOME_I] != Old_White[HOME_I]) {
- Draw_Point_And_Pieces (WHITE_PLAYER,HOME_I,White[HOME_I]) ;
- }
- if (Black[HOME_I] != Old_Black[HOME_I]) {
- Draw_Point_And_Pieces (BLACK_PLAYER,HOME_I,Black[HOME_I]) ;
- }
-
- /* Remember what this display was like */
- for (pw = 0 ; pw < N_PLACES ; pw++) {
- Old_Black[pw] = Black[pw] ;
- Old_White[pw] = White[pw] ;
- }
-
-
- }
-
- /***************************************************************************/
-
- void Clear_Point (Player_t Player, short Point_Num)
- {
- short A_Box [2][2] ; /* Defines box of the point */
-
- Get_Pbox_Coords (A_Box,Player,Point_Num) ;
- Fill_Rect (A_Box[0][XI],A_Box[0][YI],
- Grafs.Grid_Wide-1,
- A_Box[1][YI]-A_Box[0][YI],BLACK) ;
- }
-
- /***************************************************************************/
-
- void Get_Piece_Center (short* X_Center, short* Y_Center,
- Player_t Player,
- short Point_Num, short Piece_Row,
- short Total_Pieces, short Rows_Available)
- /*
- PURPOSE: To return the graphical center of the Player piece at Piece_Row,Point_Num.
- */
- {
- if (Total_Pieces <= Rows_Available) {
- /* No squeezing required */
- short Row,Col ;
-
- Get_Grid_Point (&Col,&Row,Player,Point_Num,Piece_Row) ;
- Get_Grid_Center (X_Center,Y_Center,Col,Row) ;
-
- } else {
- /* Squeezing required to get all pieces in space available. */
- short Y_Center_Start,Y_Center_End,Y_Offset ;
-
- Get_Piece_Center (X_Center,&Y_Center_Start,
- Player,Point_Num,0,Rows_Available,Rows_Available) ;
- Get_Piece_Center (X_Center,&Y_Center_End,
- Player,Point_Num,Rows_Available-1,Rows_Available,Rows_Available) ;
-
- Y_Offset = (Piece_Row*(Y_Center_End-Y_Center_Start))/(Total_Pieces-1) ;
- (*Y_Center) = Y_Center_Start + Y_Offset ;
- }
- }
-
- /***************************************************************************/
-
- char Show_Transits_And_Kb_Look (Transit_t* Me, Transit_t* Him,
- Player_t Player, Speed_t Speed)
- /*
- PURPOSE: To draw the motions contained in the transits, and to look
- at the keyboard.
- NOTES: 1) The Speed parameter tells us how fast to display the transits.
- 2) I have not yet thoiught of a simple way of seperating these
- two functions, what with the PAUSING option.
- */
- {
- char Return_Char ;
-
- if (kbhit() && Speed != PAUSING) {
- Return_Char = (char)Get_Key () ;
- } else {
- Return_Char = NUL ;
- }
-
- if (Speed == FULL_SPEED) {
- return (Return_Char) ;
- }
-
- Draw_Transits (Me, Him, Player) ; /* Draw the paths */
-
- switch (Speed) {
- case MED_SPEED :
- /* Show transits as fast as you can */
- break ;
- case SLOW_SPEED :
- /* Show transits slowly */
- Seconds_Delay (2) ;
- break ;
- case PAUSING :
- /* User hits a key half way through */
- Print_Message (HIT_MOUSE_MSG) ;
- (void)Get_User_Point_Choice (&Return_Char,BLACK_PLAYER,QUESTION_SHP) ;
- Clear_Help_Line () ;
- break ;
-
- #if DEBUG_SOURCE
- default :
- printf ("\nERROR in Show_Transits, Speed = %d",Speed) ;
- Error_Exit ("Bad Speed in Show_Transits") ;
- break ;
- #endif
-
- }
-
- Draw_Transits (Me, Him, Player) ; /* Erase the paths */
- return (Return_Char) ;
- }
-
- /***************************************************************************/
-
- void Side_Text (short Row, short Col, char* String, short Color)
- /*
- PURPOSE: To draw a string at the given row-col in the side text area.
- NOTES : 1) MSC uses text coords starting at 1, not 0, whereas this
- function is called assuming 0,0 is the top left coord, not 1,1.
- 2) MSC uses row-col addressing, not pixel addressing.
- 3) If you print on the very last character space of the screen
- MSC give you an (unwanted and unexpected) CRLF, so you have
- to avoid that.
- */
- {
- Row = Row + Disp_Cfg.Text_Rows - SIDE_ROWS ;
- Col = Col + Disp_Cfg.Text_Cols - SIDE_COLS ;
-
- Graf_Text (Row,Col,String,Color) ;
- }
-
- /***************************************************************************/
-
- void Help_Text (short Col, char* String, short Color)
- /*
- PURPOSE: To draw a string at the given col in the help text area.
- */
- {
- short Row ;
-
- Row = Disp_Cfg.Text_Rows - 1 ;
- Graf_Text (Row,Col,String,Color) ;
- }
-
- /***************************************************************************/
-
- void Clear_Help_Line (void)
- /*
- PURPOSE: To clear the single line of help at the bottom of the screen.
- */
- {
- Fill_Rect (Grafs.Help_X,Grafs.Help_Y,
- Grafs.Help_X+Grafs.Help_Wide,
- Grafs.Help_Y+Disp_Cfg.Char_High,0) ;
- }
-
- /***************************************************************************/
-
- boolean Pixel_To_Grid (short* Col, short* Row, short x, short y)
- /*
- PURPOSE: Given the pixel at x,y, set the row and col to the relevant
- grid, returning FALSE if x,y is outside of the grid.
- */
- {
- if ((x < Grafs.Board_X) || (x > Grafs.Board_X + Grafs.Board_Wide)) {
- return (FALSE) ;
- } else if ((y < Grafs.Board_Y) || (y > Grafs.Board_Y + Grafs.Board_High)) {
- return (FALSE) ;
- }
-
- x = x - Grafs.Board_X ;
- y = y - Grafs.Board_Y ;
-
- (*Col) = x / Grafs.Grid_Wide ;
- (*Row) = y / Grafs.Grid_High ;
-
- return (TRUE) ;
- }
-
- /***************************************************************************/
-
- short Grid_To_Point (short G_Col, short G_Row, short Player)
- /*
- PURPOSE: To return the point specified by the grid coords.
- NOTES: 1) We do the basic calcs assuming BLACK player and reverse
- the point if it is white at the end.
- 2) Study the diagram at the top of this file.
- */
- {
- short Point ;
-
- /* Do these simple cases first... */
- if (G_Col == BAR_COL) {
- return (BAR_I) ;
- } else if (G_Col == HOME_COL) {
- return (HOME_I) ;
- }
-
- if (G_Row < (GRID_ROWS/2)) {
- /* In top half of the board */
- if (G_Col > 6) {
- Point = G_Col - 1 ;
- } else {
- Point = G_Col ;
- }
- } else {
- /* In bottom half of the board */
- if (G_Col > 6) {
- Point = 13 + (13 - G_Col) ;
- } else {
- Point = 19 + ( 6 - G_Col) ;
- }
- }
-
- if ((Point < 0) || (Point > HOME_I)) {
- Error_Exit ("Point out of range") ;
- }
-
- if (Player == BLACK_PLAYER) {
- return (Point) ;
- } else {
- return (Reverse_Index(Point)) ;
- }
- }
-
- /***************************************************************************/
-
-